# encoding: utf-8
"""
模型预测模块
"""
import logging
import time

from ...center.clean import split_long_text
from ...tools.tool import get_offset
from ...config.base import LIMIT_PARAGRAPH


LINE_BREAK_SYMBOL = '\n'


def predict_contract_elements(infos, semantic_cls, default_schema,
                              is_relative_index=True, is_switch_data=True, correct_contract=True):
    """
    :param infos:
    :param semantic_cls:
    :param default_schema:
    :param is_relative_index:
    :param is_switch_data:
    :param correct_contract: 是否对条款进行修正以获取更好的内容
    是否需要对info 进行拼接
    :return:
    """
    predict_result = []
    time_1 = time.time()
    if not correct_contract:
        '''
        不进行内容的整改
        '''
        for info in infos:
            result_info = _predict_contract_element(info, semantic_cls, default_schema, is_switch_data, is_relative_index)
            predict_result.extend(result_info)

    else:
        '''
        采取最简单的策略, 拼接前文, 拆分长文本
        '''
        correct_info = correct_switch_contract(infos)
        head_clause = correct_info['head_clause']
        concat_clause_option = correct_info['concat_clause_option']
        new_infos = correct_info['new_infos']
        if not concat_clause_option:
            for info in new_infos:
                result_info = _predict_contract_element(info, semantic_cls, default_schema, is_switch_data,
                                                        is_relative_index)
                predict_result.extend(result_info)
        else:
            for info in new_infos:
                if info['termLevel'] == 0:
                    result_info = _predict_contract_element(info, semantic_cls, default_schema, is_switch_data,
                                                            is_relative_index)
                    predict_result.extend(result_info)
                else:
                    result_info = _predict_contract_element_concat([head_clause, info], [1], semantic_cls, default_schema, is_switch_data, is_relative_index)
                    predict_result.extend(result_info)
    time_2 = time.time()
    msg = "要素预测用时： %s" % (time_2 - time_1)
    print(msg)
    logging.info(msg)
    return predict_result

# def predict_contract_elements(infos, semantic_cls, default_schema,
#                               is_relative_index=True, is_switch_data=True, correct_contract=True):
#     """
#     :param infos:
#     :param semantic_cls:
#     :param default_schema:
#     :param is_relative_index:
#     :param is_switch_data:
#     :param correct_contract: 是否对条款进行修正以获取更好的内容
#     是否需要对info 进行拼接
#     :return:
#     """
#     predict_result = []
#     if not correct_contract:
#         for info in infos:
#             result_info = _predict_contract_element(info, semantic_cls, default_schema, is_switch_data, is_relative_index)
#             predict_result.extend(result_info)
#
#     else:
#         '''
#         采取最简单的策略, 拼接前文
#         '''
#         head_clause = None
#         for info in infos:
#             if info['termLevel'] == 0:
#                 head_clause = info
#                 break
#
#         if head_clause is None:
#             for info in infos:
#                 result_info = _predict_contract_element(info, semantic_cls, default_schema, is_switch_data,
#                                                         is_relative_index)
#                 predict_result.extend(result_info)
#         else:
#             for info in infos:
#                 if info['termLevel'] == 0:
#                     result_info = _predict_contract_element(info, semantic_cls, default_schema, is_switch_data,
#                                                             is_relative_index)
#                     predict_result.extend(result_info)
#                 else:
#                     result_info = _predict_contract_element_concat([head_clause, info], [1], semantic_cls, default_schema, is_switch_data, is_relative_index)
#                     predict_result.extend(result_info)
#     # print("infos", infos)
#     # print("default_schema", default_schema)
#     # print("predict_result", predict_result)
#     return predict_result


def correct_switch_contract(infos):
    """
    修正文本 设计文本的合并和拆分
    :return:
    """
    '''
    获取前文条款
    '''
    head_clause = None
    for info in infos:
        if info['termLevel'] == 0:
            head_clause = info
            break
    '''
    判断是否拼接前置条款
    '''
    if head_clause is None:
        concat_clause_option = False
    else:
        concat_clause_option = True
        if len(head_clause['text']) > LIMIT_PARAGRAPH * 0.5:
            # 此时如果再拼接前置文本会超长
            concat_clause_option = False

    '''
    拆分文本
    '''
    new_infos = []
    for info in infos:
        termLevel = info['termLevel']
        index = info['index']
        text = info['text']
        if len(text) > LIMIT_PARAGRAPH:
            split_texts, split_text_intervals = split_long_text(text)
            for split_text, split_text_interval in zip(split_texts, split_text_intervals):
                new_infos.append(
                    {
                        'index': index,
                        'termLevel': termLevel,
                        'split_text_interval': split_text_interval,
                        'text': split_text,
                        'origin_text': text
                    }
                )
        else:
            new_infos.append(
                {
                    'index': index,
                    'termLevel': termLevel,
                    'split_text_interval': [0, 0],
                    'text': text,
                    'origin_text': text
                }
            )

    result = {
        'concat_clause_option': concat_clause_option,
        'new_infos': new_infos,
        'head_clause': head_clause,
    }
    # print(result)
    return result


def _predict_contract_element_concat(infos, target_info_indexes, semantic_cls, default_schema,
                                     is_switch_data, is_relative_index):
    """
    对于需要拼接文本集获取获取目标位置的相对信息
    :param infos:
    :param target_info_indexes: target_index 尽量为1个, index 必须连续
    :param semantic_cls:
    :param default_schema:
    :param is_switch_data:
    :param is_relative_index:
    :return:
    """
    all_text = [_['text'] for _ in infos]
    '''
    获取目标text在all_text的位置信息
    用LINE_BREAK_SYMBOL链接text
    '''
    input_text = LINE_BREAK_SYMBOL.join(all_text)
    target_text_indexes = {}
    for target_info_index in target_info_indexes:
        target_text_indexes[target_info_index] = [sum(len(text) for text in all_text[:target_info_index]) + len(LINE_BREAK_SYMBOL)*target_info_index, sum(len(text) for text in all_text[:target_info_index + 1]) + len(LINE_BREAK_SYMBOL)*target_info_index]
    # print(target_text_indexes)
    # print([all_text[_1:_2] for _1, _2 in target_text_indexes])
    schema = default_schema
    if schema is None:
        raise ValueError('提取元素不能为空')
    if isinstance(schema, list):
        schema = dict((_, None) for _ in schema)
    predict_result = semantic_cls(
        input=input_text,
        schema=schema
    )
    output = predict_result['output']
    '''
    计算元素是否属于某一个目标集合
    '''
    target_elements = {}  # 目标的元素集合
    for _ in output:
        element_info = _[0]
        element_offset = element_info['offset']
        actual_target_index = -1
        for target_index in target_info_indexes:
            begin_target_index, end_target_index = target_text_indexes[target_index]  # 目标的起始和结尾位
            if element_offset[0] >= begin_target_index and element_offset[-1] <= end_target_index:
                actual_target_index = target_index
                # 修正位置信息
                element_info['offset'] = [_ - target_text_indexes[actual_target_index][0] for _ in element_offset]
                break
            else:
                continue
        if actual_target_index != -1:
            target_elements.setdefault(actual_target_index, [])
            target_elements[actual_target_index].append(_)
    # 获取所有结果
    result = []
    for target_index, target_element in target_elements.items():
        if len(target_element) > 0:
            index = infos[target_index]
            target_result = find_element_index(target_element, index, is_switch_data, is_relative_index)
            result.extend(target_result)
    result = remove_duplicate(result)
    return result


def _predict_contract_element(info, semantic_cls, default_schema, is_switch_data, is_relative_index):
    input_text = info['text']
    schema = info.get('schema', default_schema)
    if schema is None:
        raise ValueError('提取元素不能为空')
    if isinstance(schema, list):
        schema = dict((_, None) for _ in schema)
    predict_result = semantic_cls(
        input=input_text,
        schema=schema
    )
    output = predict_result['output']
    '''
    计算相对位置
    '''
    result = find_element_index(output, info, is_switch_data, is_relative_index)
    result = remove_duplicate(result)
    return result


def remove_duplicate(data):
    actual_data = []
    for _ in data:
        if _ not in actual_data:
            actual_data.append(_)
    return actual_data


def find_element_index(elements, info, is_switch_data, is_relative_index):
    index = info['index']
    input_text = info.get('origin_text', info['text'])
    split_text_interval = info.get('split_text_interval', [0, 0])
    result = []
    element_position_infos = {}  # 要素位置信息集合
    element_contents = []  # 要素结果集合
    for _ in elements:
        element_info = _[0]
        element_name = element_info['type']
        element_content = element_info['span']
        element_offset = element_info['offset']
        correction_offset = [_ + split_text_interval[0] for _ in element_offset]
        element_relative_index = -1
        if is_relative_index:
            if element_content not in element_contents:
                element_content_index = len(element_contents)
                element_position_infos[element_content_index] = get_offset(input_text, element_content)
                element_contents.append(element_content)
            else:
                element_content_index = element_contents.index(element_content)

            # print('element_position_infos:', element_position_infos)
            # print('element_content:', element_content)
            # print('element_offset:', element_offset)
            # print('correction_offset:', correction_offset)
            for i, relative_offset in enumerate(element_position_infos[element_content_index]):
                if relative_offset == correction_offset:
                    element_relative_index = i
            if element_relative_index == -1:
                raise Exception('数据问题, 没有找到相对位置')
        if is_switch_data:
            result.append(
                {
                    'index': index,
                    'elementName': element_name,
                    'elementContent': element_content,
                    'elementOffset': correction_offset,
                    'relativePosition': element_relative_index
                }
            )
        else:
            result.append(
                {
                    'index': index,
                    'element_name': element_name,
                    'element_content': element_content,
                    'element_offset': correction_offset,
                    'element_relative_index': element_relative_index
                }
            )
    return result


# def find_element_index(elements, info, is_switch_data, is_relative_index):
#     index = info['index']
#     input_text = info['text']
#     result = []
#     element_position_infos = {}  # 要素位置信息集合
#     element_contents = []  # 要素结果集合
#     for _ in elements:
#         element_info = _[0]
#         element_name = element_info['type']
#         element_content = element_info['span']
#         element_offset = element_info['offset']
#         element_relative_index = -1
#         if is_relative_index:
#             if element_content not in element_contents:
#                 element_content_index = len(element_contents)
#                 element_position_infos[element_content_index] = get_offset(input_text, element_content)
#                 element_contents.append(element_content)
#             else:
#                 element_content_index = element_contents.index(element_content)
#
#             for i, relative_offset in enumerate(element_position_infos[element_content_index]):
#                 if relative_offset == element_offset:
#                     element_relative_index = i
#             if element_relative_index == -1:
#                 raise Exception('数据问题, 没有找到相对位置')
#         if is_switch_data:
#             result.append(
#                 {
#                     'index': index,
#                     'elementName': element_name,
#                     'elementContent': element_content,
#                     'elementOffset': element_offset,
#                     'relativePosition': element_relative_index
#                 }
#             )
#         else:
#             result.append(
#                 {
#                     'index': index,
#                     'element_name': element_name,
#                     'element_content': element_content,
#                     'element_offset': element_offset,
#                     'element_relative_index': element_relative_index
#                 }
#             )
#     return result